
#include "nane/InputSystem/Win32Keyboard/Win32Keyboard.h"
#include "nane/WindowSystem/IWindowSystem.h"
#include "nane/core/assert.h"
#include "nane/core/log.h"

#include <algorithm>

namespace nane
{
    ////////////////////////////////////////////////////////////////////////////////////////////////
    static LRESULT CALLBACK s_wndProc( HWND _hWnd, UINT _msg, WPARAM _wParam, LPARAM _lParam )
    {
        Win32Keyboard* win32Keyboard = static_cast<Win32Keyboard*>(::GetPropA(_hWnd, "NANE_WIN32KEYBOARD"));
        NANE_ASSERT_RV( win32Keyboard != NULL, "Something very bad if this happened", 0 );
        return win32Keyboard->WndProc(_hWnd, _msg, _wParam, _lParam);
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    Win32Keyboard::Win32Keyboard()
        : m_hWnd( NULL )
        , m_wndProc( NULL )
        , m_currentMessage( 0 )
    {

    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    Win32Keyboard::~Win32Keyboard()
    {

    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    LRESULT Win32Keyboard::WndProc( HWND _hWnd, UINT _msg, WPARAM _wParam, LPARAM _lParam )
    {
        NANE_ASSERT( _hWnd == m_hWnd, "Window must be that we subscibed to" );
        if( _msg == WM_KEYDOWN && !(_lParam & 0x40000000) ) // && was up
        {
            byte kc = static_cast<byte>(((_lParam & 0x00ff0000) >> 16) + 128 * ((_lParam & 0x01000000) >> 24));
            m_keyboardMessages.push_back(KeyboardMessage(true, kc));;
        }
        else if( _msg == WM_KEYUP )
        {
            byte kc = static_cast<byte>(((_lParam & 0x00ff0000) >> 16) + 128 * ((_lParam & 0x01000000) >> 24));
            m_keyboardMessages.push_back(KeyboardMessage(false, kc));;
       }
        return WinAPI::CallWindowProc(m_wndProc, _hWnd, _msg, _wParam, _lParam);
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    bool Win32Keyboard::Initialize()
    {
        m_keyboardMessages.reserve(4);
        return true;
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    void Win32Keyboard::Shutdown()
    {
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    EInputDevice::raw_type Win32Keyboard::GetDeviceType()
    {
        EInputDevice deviceType = EInputDevice::Keyboard;
        return deviceType.raw_value();
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    bool Win32Keyboard::InitializeContext( IWindow::OSHandle _windowHandle )
    {
        HWND hWnd = static_cast<HWND>(_windowHandle);
        if( ::SetPropA(hWnd, "NANE_WIN32KEYBOARD", static_cast<HANDLE>(this)) == FALSE )
        {
            LOG_ERROR("Failed to SetProp");
            return false;
        }
        m_hWnd = hWnd;
        m_wndProc = reinterpret_cast<WNDPROC>(WinAPI::SetWindowLongPtr(m_hWnd, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(&s_wndProc)));
        return true;
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    void Win32Keyboard::ShutdownContext()
    {
        WinAPI::SetWindowLongPtr(m_hWnd, GWLP_WNDPROC, reinterpret_cast<LONG_PTR>(m_wndProc));
        m_wndProc = NULL;
        ::RemovePropA(m_hWnd, "NANE_WIN32KEYBOARD");
        m_hWnd = NULL;        
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
    bool Win32Keyboard::Poll( byte* _deviceNum, byte* _axis, int32* _value )
    {
        if( m_currentMessage >= m_keyboardMessages.size() )
        {
            m_keyboardMessages.clear();
            m_currentMessage = 0;
            MSG msg;
            if( WinAPI::PeekMessage(&msg, NULL, WM_KEYFIRST, WM_KEYLAST, PM_REMOVE) == TRUE )
            {
                ::TranslateMessage(&msg);
                WinAPI::DispatchMessage(&msg);
            }
        }
         
        if( m_currentMessage < m_keyboardMessages.size() )
        {
            *_deviceNum = 0;
            *_axis = m_keyboardMessages[m_currentMessage].key;
            *_value = m_keyboardMessages[m_currentMessage].isDown ? 1 : 0;
            m_currentMessage += 1;
            return true;
        }
        return false;
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////
}   // namespace nane
